<?php

namespace common\core\server;

use common\core\base\InvalidException;
use common\helpers\ConstantHelper;
use yii\base\InvalidConfigException;
use yii\data\Pagination;
use yii\data\Sort;
use yii\db\Exception;
use yii\db\Query;
use yii\helpers\ArrayHelper;
use yii\validators\Validator;

class ActiveRecord extends \yii\db\ActiveRecord
{

    use InvalidException;

    public $service;

    public function __construct(array $config = [])
    {
        parent::__construct($config);
        $this->on(self::EVENT_BEFORE_INSERT, [$this, 'saveBeforeInsert']);
        $this->on(self::EVENT_BEFORE_UPDATE, [$this, 'saveBeforeUpdate']);
        $this->service = new BaseServer();
    }

    protected function saveBeforeInsert($event)
    {
        $userId = -1;
        try {
            $userId = \Yii::$app->user->getId();
        } catch (\Throwable $exception) {

        }
        if ($this->hasAttribute('status') && empty($this->status)) {
            $this->status = ConstantHelper::NOT_DELETE;
        }

        if ($this->hasAttribute('modifier') && empty($this->modifier)) {
            $this->modifier = $userId;
        }

        if ($this->hasAttribute('creator') && empty($this->creator)) {
            $this->creator = $userId;
        }

        $time = time();
        if ($this->hasAttribute('created_at') && empty($this->created_at)) {
            $this->created_at = $time;
        }

        if ($this->hasAttribute('updated_at') && empty($this->updated_at)) {
            $this->updated_at = $time;
        }
    }

    protected function saveBeforeUpdate($event)
    {
        $userId = -1;
        try {
            $userId = \Yii::$app->user->getId();
        } catch (\Throwable $exception) {

        }
        if ($this->hasAttribute('modifier')) {
            $this->modifier = $userId;
        }
        if ($this->hasAttribute('updated_at')) {
            $this->updated_at = time();
        }
    }

    /**
     * table name
     *
     * @return mixed
     */
    public function table()
    {
        return '';
    }

    /**
     * 获得 32位 唯一 字符串
     *
     * 推特分布式id生成算法SnowFlake PHP 的实现
     *
     * 用于唯一主键
     *
     * @return string
     * @doc https://github.com/Sxdd/php_snowflake
     *
     */
    public function nextId()
    {
        $service_no = ArrayHelper::getValue(\Yii::$app->params, 'service_no', 1);
        try {
            $id = \PhpSnowFlake::nextId($service_no);
            $number = str_replace('-', 0, $id);
        } catch (\Throwable $exception) {
            SnowFlake::machineId($service_no);
            $number = SnowFlake::generateParticle();
            $number = str_pad($number, 32, 0, STR_PAD_LEFT);
        }
        return $number;
    }

    /**
     * 排序
     *
     * @param Query $query
     * @param null  $defaultOrder
     *
     * @return Query
     */
    public function order(Query &$query, $defaultOrder = 'created_at desc')
    {
        $model = get_called_class();
        /* @var $model ActiveRecord */
        $attributeLabels = $model::attributeLabels();
        $attributes = array_keys($attributeLabels);
        $order = new Sort([
            'attributes' => $attributes,
        ]);
        $query->orderBy($order->orders);
        if ($defaultOrder) {
            $query->addOrderBy($defaultOrder);
        }
        return $query;
    }

    /**
     * lbs 2点计算距离
     *
     * @param float  $lat 纬度
     * @param float  $lng 经度
     * @param string $latName
     * @param string $lngName
     *
     * @return string
     */
    public function distanceSql($lat, $lng, $latName = 'lat', $lngName = 'lng')
    {
        $sql = "(2 * 6371.393 * ASIN(SQRT(COS( 23.99 * PI( ) / 180 ) * COS( 23.99 * PI( ) / 180 ) * POW( SIN( ( {$lng} - {$lngName} ) * PI( ) / 360 ), 2 ) + POW( SIN( ( {$lat} - {$latName} ) * PI( ) / 360 ), 2 )) ) * 1000 ) ";
        return $sql;
    }

    /**
     * debug
     *
     * @param Query $query
     */
    public function debugForQuery(Query $query)
    {
        echo $query->createCommand()->getRawSql();
        die;
    }

    /**
     * 重构 获得第一个错误， 是否数组 还是 字符串
     *
     * @param bool $isArray
     *
     * @return array|mixed
     */
    public function getFirstErrors($isArray = false)
    {
        $error = parent::getFirstErrors();
        return $isArray ? $error : current($error);
    }

    /**
     * 重构 save 方法，如果保存有错误，跑出错误
     *
     * @param bool $isApi
     * @param bool $runValidation
     * @param null $attributeNames
     *
     * @return bool
     * @throws
     */
    public function save($isApi = null, $runValidation = true, $attributeNames = null)
    {
        if ($isApi === null) {
            $isApi = $this->isApi();
        }
        $row = parent::save($runValidation, $attributeNames);
        if (!$row && $isApi) {
            $this->invalidFormException($this->getFirstErrors());
        }
        return $row;
    }

    /**
     * 重构  setAttributes 方法，去掉 为空的数据
     *
     * @param array $values
     * @param bool  $safeOnly
     */
    public function setAttributes($values, $safeOnly = true)
    {
        foreach ($values as $key => $value) {
            if (is_null($value)) {
                unset($values[$key]);
            }
        }
        parent::setAttributes($values, $safeOnly);
    }

    /**
     * 重构 创建规则规则
     *
     * 添加 字段 trim
     *
     * @return \ArrayObject
     * @throws InvalidConfigException
     */
    public function createValidators()
    {
        $rules = $this->rules();
        $key = array_keys($this->attributeLabels());
        $rules[] = [$key, 'trim'];
        $validators = new \ArrayObject();
        foreach ($rules as $rule) {
            if ($rule instanceof Validator) {
                $validators->append($rule);
            } elseif (is_array($rule) && isset($rule[0], $rule[1])) { // attributes, validator type
                $validator = Validator::createValidator($rule[1], $this, (array)$rule[0], array_slice($rule, 2));
                $validators->append($validator);
            } else {
                throw new InvalidConfigException('Invalid validation rule: a rule must specify both attribute names and validator type.');
            }
        }
        return $validators;
    }

    /**
     * 获得 表名称
     *
     * @param string $name
     *
     * @return mixed
     */
    public function getTableName($name = '')
    {
        $name = !empty($name) ? $name : $this->tableName();
        $tableName = $this->db->quoteSql($name);
        $tableName = str_replace('`', '', $tableName);
        return $tableName;
    }

    /**
     * 通过id获得数据
     *
     * @param int    $id
     * @param string $isDeleteName 是否条件有 status
     *
     * @return array|null|\yii\db\ActiveRecord
     * @throws \yii\base\UserException
     */
    public function getById($id, $isDeleteName = 'status')
    {
        if (empty($id)) {
            $this->invalidParamException($this->table() . '不能为空');
        }
        $object = $this->find()->where([
            'id' => $id,
        ]);
        if (!empty($isDeleteName)) {
            $object->andWhere([$isDeleteName => ConstantHelper::NOT_DELETE,]);
        }
        $data = $object->one();
//        if (empty($data)) {
//            $this->invalidParamException($this->table() . '不存在');
//        }
        return $data;
    }

    public function getByIdNull($id, $isDeleteName = 'status')
    {
        if (empty($id)) {
           return [];
        }
        $object = $this->find()->where([
            'id' => $id,
        ]);
        if (!empty($isDeleteName)) {
            $object->andWhere([$isDeleteName => ConstantHelper::NOT_DELETE,]);
        }
        $data = $object->one();
        return $data;
    }

    /**
     * 通过id获得数据
     *
     * @param array  $id
     * @param string $isDeleteName 是否条件有status
     *
     * @return array|null|\yii\db\ActiveRecord
     */
    public function getByIds(array $id, $isDeleteName = 'status')
    {
        $object = $this->find()->where([
            'id' => $id,
        ]);
        if (!empty($isDeleteName)) {
            $object->andWhere([$isDeleteName => ConstantHelper::NOT_DELETE,]);
        }
        return $object->all();
    }

    /**
     * 获得有效参数
     *
     * @param array $params
     *
     * @return mixed
     * @throws \yii\base\InvalidConfigException
     */
    public function filterParams(array $params)
    {
        return ArrayHelper::getValue($params, $this->formName(), $params);
    }

    public $isAddRecord = true;

    /**
     * 添加 / 更新
     *
     * @param array  $params
     * @param string $isDeleteName
     * @param bool   $isSave
     *
     * @return array|ActiveRecord|null|\yii\db\ActiveRecord
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\base\UserException
     * @throws Exception
     */
    public function updateByParams(array $params, $isSave = true, $isDeleteName = 'status')
    {
        $params = $this->filterParams($params);
        $object = $this;
        if ($this->isAddRecord && $this->issetAndEmpty($params, 'id', true)) {
            $object = $this->getById($params['id'], $isDeleteName);
        }
        $object->scenario = $this->scenario;
        $object->setAttributes($params);
        if ($isSave) {
            $con = \Yii::$app->db->beginTransaction();
            try {
                $object->save();
                $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $object, $params);
                $con->commit();
            } catch (Exception $e) {
                $con->rollBack();
                throw $e;
            }
        }
        return $object;
    }

    /**
     * 重构 删除
     *
     * @param string $isDeleteName
     *
     * @return $this|false|int
     * @throws \Throwable
     * @throws \yii\db\StaleObjectException
     */
    public function delete($isDeleteName = 'status')
    {
        $con = \Yii::$app->db->beginTransaction();
        try {
            $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $this, $this->id);
            if (!empty($isDeleteName)) {
                $this->$isDeleteName = 1;
                $this->save();
                $model = $this;
            } else {
                $model = parent::delete();
            }
            $con->commit();
        } catch (Exception $e) {
            $con->rollBack();
            throw $e;
        }
        return $model;
    }

    /**
     * 通过id删除数据
     *
     * @param int    $id
     * @param string $isDeleteName
     *
     * @return array|null|\yii\db\ActiveRecord
     * @throws \Throwable
     * @throws \yii\base\UserException
     * @throws \yii\db\StaleObjectException
     */
    public function deleteById($id, $isDeleteName = 'status')
    {
        return $this->getById($id, $isDeleteName)->delete($isDeleteName);
    }

    /**
     * 通过ids删除数据
     *
     * @param array  $id
     * @param string $isDeleteName
     *
     * @return int
     * @throws Exception
     */
    public function deleteByIds(array $id, $isDeleteName = 'status')
    {
        $con = \Yii::$app->db->beginTransaction();
        try {
            $where = ['id' => $id];
            if (!empty($isDeleteName)) {
                $row = $this->updateAllByParams([$isDeleteName => 1], $where);
                $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $row, $where);
            } else {
                $object = $this->find()->where(['id' => $id])->all();
                $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $object, $where);
                $row = self::deleteAll($where);
            }
            $con->commit();
        } catch (Exception $e) {
            $con->rollBack();
            throw $e;
        }
        return $row;
    }

    /**
     * 批量更新
     *
     * @param array $params
     * @param array $where
     *
     * @return int
     * @throws Exception
     */
    public function updateAllByParams(array $params, array $where)
    {
        $con = \Yii::$app->db->beginTransaction();
        try {
            $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $params, $where);
            $row = $this->updateAll($params, $where);
            $con->commit();
        } catch (Exception $e) {
            $con->rollBack();
            throw $e;
        }
        return $row;
    }

    /**
     * 累加或者累减
     *
     * 更新计数
     *
     * 如果用save 高并发容易错误
     *
     * @param array  $params
     * @param string $id
     *
     * @return array|null|\yii\db\ActiveRecord
     * @throws \yii\base\UserException
     * @throws Exception
     */
    public function updateCount(array $params, $id = '')
    {
        $con = \Yii::$app->db->beginTransaction();
        try {
            $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $params, $id);
            $row = $this->isNewRecord ? $this->getById($id, false)->updateCounters($params) : $this->updateCounters($params);
            $con->commit();
        } catch (Exception $e) {
            $con->rollBack();
            throw $e;
        }
        return $row;
    }

    /**
     * 累加或者累减
     *
     * 更新所有计数
     *
     * 如果用save 高并发容易错误
     *
     * @param array $params
     * @param       $where
     *
     * @return int
     * @throws Exception
     */
    public function updateAllCount(array $params, $where)
    {
        $con = \Yii::$app->db->beginTransaction();
        try {
            $this->service->triggerServer(BaseServer::EVENT_LOG_OPERATION, $params, $where);
            $row = $this->updateAllCounters($params, $where);
            $con->commit();
        } catch (Exception $e) {
            $con->rollBack();
            throw $e;
        }
        return $row;
    }
}